package com.miage.itrading.dao;

import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import com.miage.itrading.entites.Cotation;
import com.miage.itrading.entites.Historique;
import com.miage.itrading.entites.Intraday;
import com.miage.itrading.utils.ConnecteurBDD;
import com.miage.itrading.utils.Const;
import com.miage.itrading.utils.Util;

public class CotationDAO {
	private final Cotation	cotation;

	public CotationDAO() {
		this.cotation = new Cotation();
	}

	public CotationDAO(Cotation cot) {
		this.cotation = cot;
	}

	public CotationDAO(String nom, String mnemonique) throws IOException {
		this.cotation = new Cotation();
		cotation.setNom(nom);
		cotation.setMnemonique(mnemonique);

	}
	
	/**
	 * Si la bourse est fermée, retourne la dernière cotation historique,
	 * sinon retourne la dernière valeur intraday relevée
	 * 
	 * @param idCotation	id de la cotation sur laquelle la recherche s'effectue
	 * @return
	 * @throws SQLException
	 */
	public Intraday getCotationCourante(int idCotation) throws SQLException {
		
		Intraday intra = new Intraday();
		
		/**
		 * Configuration des heures d'ouverture et de fermeture de la bourse parisienne pour aujourd'hui
		 */
		Calendar now = Calendar.getInstance();

		Calendar ouverture= Calendar.getInstance();
		ouverture.setTime(Const.HEURE_OUVERTURE);
		
		Calendar fermeture = Calendar.getInstance();
		fermeture.setTime(Const.HEURE_FERMETURE);

		ouverture.set(Calendar.YEAR, now.get(Calendar.YEAR));
		ouverture.set(Calendar.MONTH, now.get(Calendar.MONTH));
		ouverture.set(Calendar.DAY_OF_MONTH, now.get(Calendar.DAY_OF_MONTH));
		
		fermeture.set(Calendar.YEAR, now.get(Calendar.YEAR));
		fermeture.set(Calendar.MONTH, now.get(Calendar.MONTH));
		fermeture.set(Calendar.DAY_OF_MONTH, now.get(Calendar.DAY_OF_MONTH));
		
		
		ResultSet result;
		
		/**
		 * La bourse est fermée on prend la valeur de la veille
		 */
		if (now.getTime().after(fermeture.getTime()) || 
			now.getTime().before(ouverture.getTime())) {
			
			result = ConnecteurBDD.executeQuery("SELECT h.* FROM historique h WHERE h.idcotation = " + 
										 		idCotation + " ORDER BY h.date DESC LIMIT 0 , 1");
			
			if (result.next()) {
				intra.setCotation(result.getFloat("fermeture"));
				intra.setVolume(result.getLong("volume"));
				intra.setDate(Util.getDateBySQL(result.getTimestamp("date")));
			}
		}
		else {
			result = ConnecteurBDD.executeQuery("SELECT tr.* FROM tempsreel tr " +
												"WHERE 		tr.idcotation = " + idCotation + " " + 
												"AND 		tr.date >= CURDATE() " +
												"ORDER BY tr.date DESC LIMIT 0, 1");
			
			if (result.next()) {
				intra.setCotation(result.getFloat("cotation"));
				intra.setVolume(result.getLong("volume"));
				intra.setDate(Util.getDateBySQL(result.getTimestamp("date")));
			}
		}
		
		return intra;
	}

	@SuppressWarnings("deprecation")
	public void ajouterHistorique(String date, String ouverture,
			String fermeture, String plusBas, String plusHaut, String volume) {
		Historique h = new Historique();

		h.setDate(new Timestamp((Integer.parseInt(date.split("-")[0])) - 1900,
				Integer.parseInt(date.split("-")[1]) - 1, Integer.parseInt(date
						.split("-")[2]), 17, 35, 0, 0));
		h.setOuverture(Float.parseFloat(ouverture));
		h.setFermeture(Float.parseFloat(fermeture));
		h.setPlusBas(Float.parseFloat(plusBas));
		h.setPlusHaut(Float.parseFloat(plusHaut));
		h.setVolume(Float.parseFloat(volume));

		cotation.getHistoriques().add(h);
	}

	public Historique donneeTableau(int i) {
		Historique retour = null;
		if (cotation.getHistoriques().get(i) != null) {
			retour = cotation.getHistoriques().get(i);
		}

		return retour;
	}

	public void ecrireCotation() throws SQLException {

		if (getCotationByName(cotation.getNom()) != null) {

			// Vérifie l'existence de la cotation avant l'insertion
			ResultSet result = ConnecteurBDD.executeQuery(
					"SELECT idcotation FROM cotation WHERE nom = '"
							+ cotation.getNom() + "' AND mnemonique = '"
							+ cotation.getMnemonique() + "'");

			if (!result.next()) {
				ConnecteurBDD.executeUpdate(
						"INSERT INTO cotation (nom, mnemonique) VALUES ('" + cotation.getNom() + "', '" + cotation.getMnemonique() + "')");
			}

			result.close();
		}

		Cotation transcient = new Cotation();
		transcient = getCotationByName(cotation.getNom());

		final String SQLinsert = "INSERT INTO historique (idcotation, date, ouverture, fermeture, plusbas, plushaut, volume) VALUES ";
		String SQLvalues;

		/**
		 * Insère les enregistrements par lots de 1000 pour accélérer le
		 * traitement
		 */

		Historique h;
		int nbEnregistrements = cotation.getHistoriques().size();

		for (int i = 0; i < nbEnregistrements; ++i) {

			SQLvalues = "";
			while (((i % Const.NB_ENREGISTREMENTS_GROUPE != 0) || (i == 0))
					&& (i < nbEnregistrements)) {
				h = cotation.getHistoriques().get(i);

				SQLvalues += "(" + transcient.getIdCotation() + ",'"
						+ h.getDate() + "'," + h.getOuverture() + ","
						+ h.getFermeture() + "," + h.getPlusBas() + ","
						+ h.getPlusHaut() + "," + h.getVolume() + "),";

				++i;
			}

			// Retire la derniere ","
			SQLvalues = SQLvalues.substring(0, SQLvalues.length() - 1);

			ConnecteurBDD.executeUpdate(SQLinsert + SQLvalues);
		}
	}

	/**
	 * 
	 * @param indice
	 *            id de l'indice
	 * @return valeurs qui composent l'indice, triées par nom asc
	 * @throws SQLException
	 */
	public List<Integer> getCompositionIndice(int indice) throws SQLException {
		ResultSet result = ConnecteurBDD.executeQuery(
						"SELECT i.idcotation FROM indice i, cotation c " +
						"WHERE i.idindice = " + indice + " " +
						"AND i.idcotation = c.idcotation " +
						"ORDER BY c.nom ASC");

		ArrayList<Integer> composition = new ArrayList<Integer>();
		while (result.next()) {
			composition.add(result.getInt("IdIndice"));
		}

		return composition;
	}

	public Cotation getCotation() {
		return cotation;

	}

	
	/**
	 * Fonction commune à tous les getCotationBy
	 * 
	 * @param rs
	 * @param bdd
	 * @return
	 * @throws SQLException
	 */
	private Cotation getCotation(ResultSet rs) throws SQLException {
		Cotation c = new Cotation();
		if (rs.next()) {
			c = bindCotation(rs);
		}
		
		Util.addCotationCache(c);
		return c;
	}
	
	private Cotation bindCotation(ResultSet rs) throws SQLException {

		Cotation c = new Cotation();

		c.setIdCotation(rs.getInt("idcotation"));
		c.setMnemonique(rs.getString("mnemonique"));
		c.setMnemonique2(rs.getString("mnemonique2"));
		c.setNom(rs.getString("nom"));
		

		/**
		 * Récupération de TOUT l'historique
		 */

		ResultSet rs2 = ConnecteurBDD.executeQuery(
				"SELECT * FROM historique WHERE (idcotation = " + c.getIdCotation() + ") ORDER BY date ASC");
		
		List<Historique> historiques = new ArrayList<Historique>();
		while (rs2.next()) {

			Historique tempHistorique = new Historique();
			tempHistorique.setDate(rs2.getTimestamp("date"));
			tempHistorique.setOuverture(rs2.getFloat("ouverture"));
			tempHistorique.setFermeture(rs2.getFloat("fermeture"));
			tempHistorique.setPlusBas(rs2.getFloat("plusbas"));
			tempHistorique.setPlusHaut(rs2.getFloat("plushaut"));
			tempHistorique.setVolume(rs2.getFloat("volume"));

			historiques.add(tempHistorique);
		}
		
		c.setHistoriques(historiques);
		
		/**
		 * Récupération des valeurs intraday du jour
		 */
		ResultSet rs3 = ConnecteurBDD.executeQuery(
				"SELECT * FROM tempsreel " +
				"WHERE (idcotation = " + c.getIdCotation() + ") " +
				"AND date >= CURDATE() " +				
				"ORDER BY date ASC");
		
		List<Intraday> intraday = new ArrayList<Intraday>();


		while (rs3.next()) {

			intraday.add(new Intraday(rs3.getInt("idcotation"), 
									  Util.getDateBySQL(rs3.getTimestamp("date")),
									  rs3.getFloat("cotation"), 
									  rs3.getLong("cotation")));
		}
		
		c.setIntradays(intraday);
		
		return c;
	}
	
	public Cotation getCotationById(int idcot) throws SQLException {
		
		if (Util.isCotationCached(idcot)) {
			return Util.getCotation(idcot);
		}
		else {
			ResultSet rs = ConnecteurBDD.executeQuery("SELECT * FROM cotation WHERE idcotation = " + idcot);

			return getCotation(rs);			
		}

	}

	public Cotation getCotationByMnemo(String name) throws SQLException {
		
		if (Util.isCotationCached(name)) {
			return Util.getCotation(name);
		}
		else {
			ResultSet rs = ConnecteurBDD.executeQuery(
					"SELECT * FROM cotation WHERE mnemonique LIKE '%" + name + "%' OR mnemonique2 LIKE '%" + name + "%'");

			return getCotation(rs);
		}
	}

	public Cotation getCotationByName(String name) throws SQLException {
		if (Util.isCotationCached(name)) {
			return Util.getCotation(name);
		}
		else {
			ResultSet rs = ConnecteurBDD.executeQuery("SELECT * FROM cotation c WHERE c.nom LIKE '%" + name+ "%'");
	
			return getCotation(rs);
		}
	}

	/**
	 * 
	 * @param idCotation
	 * @param range
	 *            , intervalle a récupérer, NULL si tout l'historique est à récupérer
	 * @return
	 * @throws SQLException
	 */
	public List<Historique> getHistorique(int idCotation, Date start, Date end)
			throws SQLException {

		String sqlOrder;

		if (start.equals(null) && end.equals(null)) {
			sqlOrder = "SELECT * FROM historique WHERE idcotation = " + idCotation + " ORDER BY date ASC";
			
		} else if (end.equals(null)) {
			sqlOrder = "SELECT * FROM historique WHERE idcotation = " + idCotation + " AND date >= '" + Util.getTimeStampByJavaDate(start) + "' ORDER BY date ASC";
		} else {
			sqlOrder = "SELECT * FROM historique WHERE idcotation = "
					+ idCotation + " AND date BETWEEN '"
					+ Util.getTimeStampByJavaDate(start) + "' AND '"
					+ Util.getTimeStampByJavaDate(end) + "' ORDER BY date ASC";
		}

		ResultSet result = ConnecteurBDD.executeQuery(sqlOrder);

		ArrayList<Historique> histo = new ArrayList<Historique>();
		while (result.next()) {

			Historique tempHistorique = new Historique();

			tempHistorique.setDate(result.getTimestamp("date"));
			tempHistorique.setOuverture(result.getFloat("ouverture"));
			tempHistorique.setFermeture(result.getFloat("fermeture"));
			tempHistorique.setPlusBas(result.getFloat("plusbas"));
			tempHistorique.setPlusHaut(result.getFloat("plushaut"));
			tempHistorique.setVolume(result.getFloat("volume"));

			histo.add(tempHistorique);
		}

		return histo;
	}
	
	/**
	 * 
	 * @param idCotation
	 * @param range
	 *            , intervalle a récupérer, NULL si tout l'historique est à récupérer
	 * @return
	 * @throws SQLException
	 */
	@SuppressWarnings("deprecation")
	public List<Historique> getHistoriqueWithToday(int idCotation, Date start, Date end) throws SQLException
	{
		Date today = Util.getToday();
		Calendar cal = Calendar.getInstance();
		cal.setTime(today);
		cal.set(Calendar.HOUR_OF_DAY, Const.HEURE_FERMETURE.getHours());
		cal.set(Calendar.MINUTE, Const.HEURE_FERMETURE.getMinutes());
		cal.set(Calendar.SECOND, Const.HEURE_FERMETURE.getSeconds());
		today = cal.getTime();
		
		List<Historique> histos = getHistorique(idCotation, start, end);
		if(end.equals(null) || !today.after(end))
		{
			String fromWhere = " FROM tempsreel WHERE";
			fromWhere += " idcotation = "+idCotation;
			fromWhere += " AND date BETWEEN \""+Util.getDateForSql(today)+" 00:00:00\" AND \""+Util.getDateForSql(today)+" 23:59:59\"";
			
			//try
			//{
				ResultSet rsMinMax 	= ConnecteurBDD.executeQuery("SELECT min(cotation) as min, max(cotation) as max"+fromWhere);
				ResultSet rsOuv 	= ConnecteurBDD.executeQuery("SELECT cotation as cot"+fromWhere+" ORDER BY date ASC  LIMIT 1");
				ResultSet rsClot 	= ConnecteurBDD.executeQuery("SELECT cotation as cot"+fromWhere+" ORDER BY date DESC LIMIT 1");
				
				rsMinMax.next();
				rsOuv.next();
				rsClot.next();
				
				Historique histoToday = new Historique();
				histoToday.setDate(Util.getTimeStampByJavaDate(today));
				histoToday.setOuverture	(rsOuv.getFloat("cot"));
				histoToday.setFermeture	(rsClot.getFloat("cot"));
				histoToday.setPlusHaut	(rsMinMax.getFloat("max"));
				histoToday.setPlusBas	(rsMinMax.getFloat("min"));
				
				System.out.println("histo ajouté à partir de l'intraday : ouv:"+histoToday.getOuverture()+" clot:"+histoToday.getFermeture()+" +h:"+histoToday.getPlusHaut()+" +b:"+histoToday.getPlusBas());
				histos.add(histoToday);
			//}
			//catch (SQLException e) {
				//System.out.println("Impossible de récuperer la cotation du jour");
			//}
		}
		//System.exit(0);
		return histos;
	}

	public List<Integer> getIdsIndices() throws SQLException {

		ResultSet result = ConnecteurBDD.executeQuery("SELECT DISTINCT IdIndice FROM indice");

		ArrayList<Integer> indices = new ArrayList<Integer>();
		while (result.next()) {
			indices.add(result.getInt("idindice"));
		}

		return indices;
	}

	/**
	 * Valeurs composant un indice
	 * 
	 * @param idIndice
	 * @return liste des valeurs, triés par leur nom
	 * @throws SQLException
	 */
	public List<Integer> getIndiceComposition(int idIndice) throws SQLException {

		ResultSet result = ConnecteurBDD.executeQuery(
						"SELECT i.idcotation FROM indice i, cotation c " +
						"WHERE i.idindice = " + idIndice + " " +
						"AND c.idcotation = i.idcotation " +
						"ORDER BY c.nom ASC");

		ArrayList<Integer> indices = new ArrayList<Integer>();
		while (result.next()) {
			indices.add(result.getInt("idcotation"));
		}

		return indices;
	}

//	public ArrayList<Intraday> getIntraday(int idCotation, Date start)
//			throws SQLException {
//
//		ResultSet result;
//		String sqlOrder;
//
//		if (start == null) {
//			sqlOrder = "SELECT tr.* FROM tempsreel tr WHERE tr.idcotation = "
//					+ idCotation + " AND tr.date >= CURDATE() ORDER BY tr.date ASC";
//		} else {
//			sqlOrder = "SELECT h.* FROM historique h WHERE h.idcotation = "
//					+ idCotation + " AND h.date >= '"
//					+ Util.getTimeStampByJavaDate(start) + "' ORDER BY h.date ASC";
//		}
//
//		System.out.println("Intraday : " + sqlOrder);
//
//		result = ConnecteurBDD.executeQuery(sqlOrder);
//
//		ArrayList<Intraday> histo = new ArrayList<Intraday>();
//		while (result.next()) {
//
//			Intraday intra = new Intraday();
//
//			intra.setCotation(result.getFloat("cotation"));
//			intra.setVolume(result.getLong("volume"));
//			intra.setDate(Util.getDateBySQL(result.getTimestamp("date")));
//
//			histo.add(intra);
//		}
//
//		return histo;
//	}

	public int tailleTableau() {
		return cotation.getHistoriques().size();
	}
	
	public List<Integer> getAllIdsCotations() throws SQLException
	{
		String sql = "SELECT idcotation " +
					 "FROM cotation " +
					 "ORDER BY nom ASC";
		
		ResultSet result = ConnecteurBDD.executeQuery(sql);

		ArrayList<Integer> cots = new ArrayList<Integer>();
		
		while (result.next()) {				
			cots.add(result.getInt(1));
		}
		
		return cots;		
	}
	
	public List<Cotation> getAllCotations() throws SQLException
	{
		String sql = "SELECT * FROM cotation ORDER BY nom ASC";
		ResultSet result = ConnecteurBDD.executeQuery(sql);

		ArrayList<Cotation> cots = new ArrayList<Cotation>();
		while (result.next()) {
			if (!Util.isCotationCached(result.getInt("idcotation"))) {
				cots.add(bindCotation(result));
				
				Util.addCotationCache(cots.get(cots.size() - 1));
			}
			else {
				cots.add(Util.getCotation(result.getInt("idcotation")));
			}
		}
		
		return cots;
	}
}
